
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Diagnostics;
using System.Threading;
using System.Collections;
using Swaf.Container;
using Swaf.Crypto;

namespace Swaf.Messaging
{
	/// <summary>
	/// Summary description for BaseHelper.
	/// </summary>
	public abstract class MessagingSystemBase : IMessagingSystem
	{
		private int s_defaultTimeout = 30000; //IBM MQ timeout is specified in ms
		public const string MessageEncryptedIdentifier = "[Encrypted:::";
		private ICryptoManager m_crypto = (ICryptoManager)Application.currentApp.appData["sysObjs-Crypto"];

		protected static readonly string s_queueExpireDefault = "5 min";
		protected static string s_expireQueueOption = String.Empty;
		protected static IDictionary s_connectionInfoCatalog = new ThreadSafeHashtable();

		static MessagingSystemBase()
		{
			string queueExpire = Application.currentApp.resMgr.resolve("$QueueExpiration$");
			queueExpire = (queueExpire == null || queueExpire == string.Empty || queueExpire == "$QueueExpiration$")?s_queueExpireDefault:queueExpire;

			s_expireQueueOption = string.Format("expire after {0} no use", queueExpire);
		}

		public static string CheckForEncrypt(string message)
		{
			string tmp="none";
			return CheckForEncrypt(message,tmp);		
		}
		public static string CheckForEncrypt(string message, string encrypt)
		{
			if (message.StartsWith(MessagingSystemBase.MessageEncryptedIdentifier))
			{
				int startPos=MessagingSystemBase.MessageEncryptedIdentifier.Length;
				int endPos = message.IndexOf("]",MessagingSystemBase.MessageEncryptedIdentifier.Length);
				if (startPos == endPos)
					encrypt=""; //Default
				else
					encrypt=message.Substring(startPos,endPos -1);
				ICryptoManager crypto = (ICryptoManager)Application.currentApp.appData["sysObjs-Crypto"];
				string encryptedString=message.Substring(endPos+1);
				return crypto.decrypt(encrypt,encryptedString);
			}
			return message;
		}

		public static FlexiMap getThreadCache()
		{
			LocalDataStoreSlot cacheThreadSlot = Thread.GetNamedDataSlot(Swaf.BizRule.BasicExecutor.CACHE_THREAD_SLOT_NAME);
			FlexiMap cache = (FlexiMap)Thread.GetData(cacheThreadSlot);
			if(cache == null)
			{
				cache = new FlexiMap();
				Thread.SetData(cacheThreadSlot, cache);
			}

			Debug.Assert(cache != null);
			return cache;
		}

		public static IMessagingSystem getThreadSafeMessagingSystem(string messagingSystemId)
		{
			Debug.Assert(messagingSystemId != null);
			string key = "msgSys-" + messagingSystemId;
			INameAddressSupport cache = getThreadCache();
			IMessagingSystem msgSys = (IMessagingSystem)cache.get(key);
			if(msgSys == null)
			{
				msgSys = (IMessagingSystem)Application.globalApp.factories["MessagingSystems"].createFromNickName(messagingSystemId);
				cache.put(key, msgSys);
			}
			Debug.Assert(msgSys != null);
			
			return msgSys;
		}
		
		public MessagingSystemBase()
		{
			//Intentionally left blank
		}

		public void defineConnection(string key, IMessagingConnectionInfo connectionInfo)
		{
			Debug.Assert(key != null);

			if(connectionInfo == null)
				removeConnectionInfo(key);
			else
				s_connectionInfoCatalog[key] = connectionInfo;
		}

		public static IMessagingConnectionInfo findConnectionInfo(string key)
		{
			Debug.Assert(key != null);
			return (IMessagingConnectionInfo)s_connectionInfoCatalog[key];
		}

		public IMessagingConnectionInfo getConnectionInfo(string key)
		{
			return MessagingSystemBase.findConnectionInfo(key);
		}

		public IMessagingConnectionInfo removeConnectionInfo(string key)
		{
			Debug.Assert(key != null);
			IMessagingConnectionInfo s = getConnectionInfo(key);
			if(s != null)
				s_connectionInfoCatalog.Remove(key);

			return s;
		}

		public IDictionary ConnectionDefinitions {get{return s_connectionInfoCatalog;}}

		public void send(string msgBody, string encrypt, string destination, string connectionName, string messageProtocolFormat, Swaf.Container.INameAddressSupport info)
		{
			send(msgBody, null,encrypt,destination,connectionName,messageProtocolFormat,info);
		}
		public void send(string msgBody, IDictionary messageProperties, string encrypt, string destination, string connectionName, string messageProtocolFormat, Swaf.Container.INameAddressSupport info)
		{
			lock(this)
			{
				try
				{
					open(connectionName);

					IMessageResolver resolver = (IMessageResolver)Application.currentApp.factories["MessageResolvers"].createFromNickName(messageProtocolFormat);
					string resolvedMsgBody = resolver.format(msgBody, info);
					if (!encrypt.Equals("none")  && m_crypto != null)
						resolvedMsgBody = MessagingSystemBase.MessageEncryptedIdentifier + encrypt + "]" + m_crypto.encrypt(encrypt,resolvedMsgBody);
					IMessage message = createMessage(resolvedMsgBody, messageProperties);
					publishMessage(message, destination); 

					close();

				}
				catch (Exception e)
				{
					throw;
				}
			}
		}

		public void send(string connectionKey, string msgBody, string encrypt, IDictionary messageProperties)
		{
			send(msgBody, encrypt, getConnectionInfo(connectionKey), messageProperties);
		}

		public void send(string msgBody, string encrypt, IMessagingConnectionInfo connectionInfo)
		{
			send(msgBody, encrypt, connectionInfo, null);
		}

		public void send(string msgBody, string encrypt, IMessagingConnectionInfo connectionInfo, IDictionary messageProperties)
		{
			lock(this)
			{
				try
				{
					open(connectionInfo);
					string resolvedMsgBody = msgBody;
					if (!encrypt.Equals("none")  && m_crypto != null)
						resolvedMsgBody = MessagingSystemBase.MessageEncryptedIdentifier + encrypt + "]" + m_crypto.encrypt(encrypt,resolvedMsgBody);
					IMessage message = createMessage(resolvedMsgBody, messageProperties);
					publishMessage(message, connectionInfo.QueueName); 

					close();

				}
				catch (Exception e)
				{
					throw;
				}
			}
		}
		public void send(IMessage msg, IMessagingConnectionInfo connectionInfo)
		{
			open(connectionInfo);
			publishMessage(connectionInfo, msg,connectionInfo.QueueName);
			close();
		}
		public string send(string msgBody, string encrypt, string destination, string connectionName, string messageProtocolFormat, Swaf.Container.INameAddressSupport info, string replyDestination, string replyQueueManager)
		{
			return send(msgBody,null,encrypt,destination,connectionName,messageProtocolFormat,info,replyDestination,replyQueueManager);
		}

		public string send(string msgBody, IDictionary messageProperties,string encrypt, string destination, string connectionName, string messageProtocolFormat, Swaf.Container.INameAddressSupport info, string replyDestination, string replyQueueManager)
		{
			lock(this)
			{
				string resultString = "";
				IMessage sentMessage = null;
				try
				{
					open(connectionName);

					IMessageResolver resolver = (IMessageResolver)Application.currentApp.factories["MessageResolvers"].createFromNickName(messageProtocolFormat);
					string resolvedMsgBody = resolver.format(msgBody, info);
					if (!encrypt.Equals("none") && m_crypto != null)
						resolvedMsgBody = MessagingSystemBase.MessageEncryptedIdentifier + encrypt + "]" + m_crypto.encrypt(encrypt,resolvedMsgBody);
		
					sentMessage = createMessage(resolvedMsgBody, messageProperties);
					publishMessage(sentMessage, destination, replyDestination, replyQueueManager); 
				}
				catch (Exception e)
				{
					throw;
				}
				finally
				{
					close();
				}
				
				if(sentMessage != null)
				{
					IMessage results = receive(replyDestination, connectionName, sentMessage.GetCorrelationID());
					if (results != null)
						resultString = CheckForEncrypt(results.ToString());
				}
				return resultString;
			}
		}
		public IMessage receive(string source, string connectionName)
		{
			return receive(source,connectionName, s_defaultTimeout);
		}

		public IMessage receive(string source, string connectionName, int timeoutInMS)
		{
			lock(this)
			{
				IMessage resultMessage = null;
				//string resultMessage = "";

				try 
				{
					open( connectionName);
					resultMessage = GetMessage(source, timeoutInMS);
				}
				catch (Exception e)
				{
					//TAC Handle exception
					throw;
				}
				finally
				{
					close();
				}
				return resultMessage;
			}			
		}

		public IMessage receive(string connectionKey, int timeoutInMS)
		{
			return receive(getConnectionInfo(connectionKey), timeoutInMS);
		}

		public IMessage receive(IMessagingConnectionInfo info, int timeoutInMS)
		{
			lock(this)
			{
				IMessage resultMessage = null;
				//string resultMessage = "";

				try 
				{
					open(info);
					if(info.CorrelationID == null || info.CorrelationID.Length == 0)
						resultMessage = GetMessage(info, info.QueueName, timeoutInMS);
					else
						resultMessage = GetMessage(info, info.QueueName, info.CorrelationID, timeoutInMS);
				}
				catch (Exception e)
				{
					//TAC Handle exception
					throw;
				}
				finally
				{
					close();
				}
				return resultMessage;
			}
		}

		public IMessage receive(string source, string connectionName, object correlationID)
		{
			return receive(source, connectionName, correlationID, s_defaultTimeout);
		}

		public IMessage receive(string source, string connectionName, object correlationID, int timeoutInMS)
		{
			lock(this)
			{
				IMessage resultMessage = null;
				//string resultMessage = "";

				try 
				{
					open(connectionName);
					resultMessage = GetMessage(source, correlationID, timeoutInMS);
				}
				catch (Exception e)
				{
					//TAC Handle exception
					throw;
				}
				finally
				{
					close();
				}
				return resultMessage;
			}
		}
		
		public void reply(string msgBody, string destination, IMessage originalMessage)
		{
			lock(this)
			{
				//Set replyDestination to that passed by original message, if none, then use that passed in by reply call
				string replyDestination = originalMessage.GetDestination();
				if( replyDestination == "")
					replyDestination = destination;
				IMessagingConnectionInfo connection = originalMessage.GetConnectionInfo();

				//string replyQueueManager = (string) connection.get("QueueManager");
				//if( replyQueueManager == "")
					//replyQueueManager = queueManager;

				open(connection);

				try
				{
					IMessage message = createMessage(msgBody, null);
					publishReply(connection, message, replyDestination, originalMessage);
				}
				finally
				{
					close();
				}
			}
		}

		public abstract void beginTrans(IMessagingConnectionInfo connection);
		public abstract void rollback(IMessagingConnectionInfo connection);
		public abstract void commit(IMessagingConnectionInfo connection);

		protected virtual IMessage GetMessage(IMessagingConnectionInfo connection, string source, int timeoutInMS)
		{
			return GetMessage(source, timeoutInMS);
		}
		protected abstract IMessage GetMessage(string source, int timeoutInMS);
		protected virtual IMessage GetMessage(IMessagingConnectionInfo connection, string source, object correlationID, int timeoutInMS)
		{
			return GetMessage(source, correlationID,timeoutInMS);
		}
		protected abstract IMessage GetMessage(string source, object correlationID, int timeoutInMS);
		protected abstract IMessage createMessage(string msgBody, IDictionary messageProperties);
		protected abstract void open(string connectionName);
		protected abstract void open(IMessagingConnectionInfo connection);
		protected abstract void close();
		protected virtual void publishMessage(IMessagingConnectionInfo connection, IMessage objMessage, string destination)
		{
			publishMessage(objMessage,destination);
		}
		protected abstract void publishMessage(IMessage objMessage, string destination);
		protected virtual void publishMessage(IMessagingConnectionInfo connection, IMessage objMessage, string destination, string replyDestination, string replyConnection)
		{
			publishMessage(objMessage, destination,replyDestination,replyConnection);
		}
		protected abstract void publishMessage(IMessage objMessage, string destination, string replyDestination, string replyConnection);
		protected virtual void publishReply(IMessagingConnectionInfo connection, IMessage objMessage, string destination, IMessage originalMessage)
		{
			publishReply(objMessage,destination,originalMessage);
		}
		protected abstract void publishReply(IMessage objMessage, string destination, IMessage originalMessage);
	}
}
